// 版权所有2021 Go作者。版权所有。
// 此源代码的使用受BSD样式的约束
// 可以在许可证文件中找到的许可证。

package noder

import (
	"fmt"
	"os"

	"cmd/compile/internal/base"
	"cmd/compile/internal/dwarfgen"
	"cmd/compile/internal/ir"
	"cmd/compile/internal/syntax"
	"cmd/compile/internal/typecheck"
	"cmd/compile/internal/types"
	"cmd/compile/internal/types2"
	"cmd/internal/src"
)

// check2 type使用types2检查Go包，然后生成IR
// 使用结果。
func check2(noders []*noder) {
	if base.SyntaxErrors() != 0 {
		base.ErrorExit()
	}

	// 设置和语法错误报告
	var m posMap
	files := make([]*syntax.File, len(noders))
	for i, p := range noders {
		m.join(&p.posMap)
		files[i] = p.file
	}

	// 打字检查
	conf := types2.Config{
		GoVersion:             base.Flag.Lang,
		IgnoreLabels:          true, // 已通过syntax.checkbranchs模式检查解析器
		CompilerErrorMessages: true, // 使用与现有编译器错误匹配的错误字符串
		Error: func(err error) {
			terr := err.(types2.Error)
			base.ErrorfAt(m.makeXPos(terr.Pos), "%s", terr.Msg)
		},
		Importer: &gcimports{
			packages: make(map[string]*types2.Package),
		},
		Sizes: &gcSizes{},
	}
	info := types2.Info{
		Types:      make(map[syntax.Expr]types2.TypeAndValue),
		Defs:       make(map[*syntax.Name]types2.Object),
		Uses:       make(map[*syntax.Name]types2.Object),
		Selections: make(map[*syntax.SelectorExpr]*types2.Selection),
		Implicits:  make(map[syntax.Node]types2.Object),
		Scopes:     make(map[syntax.Node]*types2.Scope),
		Inferred:   make(map[syntax.Expr]types2.Inferred),
		// 根据需要扩展
	}
	pkg, err := conf.Check(base.Ctxt.Pkgpath, files, &info)
	files = nil
	base.ExitIfErrors()
	if err != nil {
		base.FatalfAt(src.NoXPos, "conf.Check error: %v", err)
	}
	if base.Flag.G < 2 {
		os.Exit(0)
	}

	g := irgen{
		target: typecheck.Target,
		self:   pkg,
		info:   &info,
		posMap: m,
		objs:   make(map[types2.Object]*ir.Name),
		typs:   make(map[types2.Type]*types.Type),
	}
	g.generate(noders)

	if base.Flag.G < 3 {
		os.Exit(0)
	}
}

type irgen struct {
	target *ir.Package
	self   *types2.Package
	info   *types2.Info

	posMap
	objs   map[types2.Object]*ir.Name
	typs   map[types2.Type]*types.Type
	marker dwarfgen.ScopeMarker

	// 应实例化其方法的完全实例化泛型类型
	instTypeList []*types.Type
}

func (g *irgen) generate(noders []*noder) {
	types.LocalPkg.Name = g.self.Name()
	typecheck.TypecheckAllowed = true

	// 在设置基础类型之前，防止大小计算
	// 对于所有包块定义的类型。
	types.DeferCheckSize()

	// 此时，types2已经处理了名称解析和
	// 类型检查。我们只需要从它的对象和类型映射
	// 表示当前由其他用户使用的内容
	// 编译器。这主要发生在3次传球中。

	// 1.处理所有进口申报。我们使用编译器自己的
	// 导入器，而不是types2的gcimporter派生的导入器，
	// 正确处理扩展和内联函数体。
	// None
	// 此外，我们还需要在单独的过程中执行此操作，因为映射是
	// 按需实例化。如果我们交错处理导入
	// 声明和其他声明，很可能我们最终会
	// 希望从另一个源文件映射对象/类型，但不希望
	// 然而，我们拥有它所依赖的进口数据。
	declLists := make([][]syntax.Decl, len(noders))
Outer:
	for i, p := range noders {
		g.pragmaFlags(p.file.Pragma, ir.GoBuildPragma)
		for j, decl := range p.file.DeclList {
			switch decl := decl.(type) {
			case *syntax.ImportDecl:
				g.importDecl(p, decl)
			default:
				declLists[i] = p.file.DeclList[j:]
				continue Outer // 没有更多重要的ECL了
			}
		}
	}
	types.LocalPkg.Height = myheight

	// 2.处理所有包块类型声明。与进口一样,，
	// 我们需要确保所有类型都正确实例化
	// 尝试映射任何使用它们的表达式。特别地，
	// 我们需要确保已经知道类型pragma（参见注释）
	// 在irgen.typeDecl中）。
	// None
	// 我们也许可以推迟包块的处理
	// 变量初始值设定项和函数体，就像noder一样，但是
	// 特殊的case-just-package块类型声明将
	// 处理包块和函数作用域之间的差异
	// 声明。
	for _, declList := range declLists {
		for _, decl := range declList {
			switch decl := decl.(type) {
			case *syntax.TypeDecl:
				g.typeDecl((*ir.Nodes)(&g.target.Decls), decl)
			}
		}
	}
	types.ResumeCheckSize()

	// 3.处理所有剩余的声明。
	for _, declList := range declLists {
		g.target.Decls = append(g.target.Decls, g.decls(declList)...)
	}

	if base.Flag.W > 1 {
		for _, n := range g.target.Decls {
			s := fmt.Sprintf("\nafter noder2 %v", n)
			ir.Dump(s, n)
		}
	}

	typecheck.DeclareUniverse()

	for _, p := range noders {
		// 进程链接名和cgo pragmas。
		p.processPragmas()

		// 仔细检查是否存在任何类型检查不一致。这可能是
		// 一旦我们对IR生成结果有信心，将删除。
		syntax.Walk(p.file, func(n syntax.Node) bool {
			g.validate(n)
			return false
		})
	}

	// 创建任何需要的通用函数模板
	g.stencil()

	// 现在，从g.target.Decl中删除所有通用函数，因为它们
	// 已用于制版，但不编译。托多：我们会的
	// 最终导出任何可导出的泛型函数。
	j := 0
	for i, decl := range g.target.Decls {
		if decl.Op() != ir.ODCLFUNC || !decl.Type().HasTParam() {
			g.target.Decls[j] = g.target.Decls[i]
			j++
		}
	}
	g.target.Decls = g.target.Decls[:j]
}

func (g *irgen) unhandled(what string, p poser) {
	base.FatalfAt(g.pos(p), "unhandled %s: %T", what, p)
	panic("unreachable")
}
